package wortubung.lang;

import static wortubung.lang.de.DeutschSpracheWerkzeug.*;
import static wortubung.lang.de.Deklination.*;
import static wortubung.util.LatinScript.*;
import static wortubung.util.CommonUtils.*;
import static wortubung.util.MatrixPrinter.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import wortubung.core.LANG;
import wortubung.core.POSInfo;
import wortubung.core.WORD;
import wortubung.inflection.DECLENSION;
import wortubung.inflection.DeclensionResult;
import wortubung.inflection.VerbConjugation;
import wortubung.inflection.external.GermanConjugationByReversoNet;
import wortubung.lang.de.Konjugierer;
import wortubung.lang.de.NomenDeklinationRegel;
import wortubung.lang.de.StarkeVerbKonjugierer;
import wortubung.util.MatrixPrinter;
import wortubung.util.Printable;

/**
 * Singleton
 */
public class LangGermanManager extends LanguageManager {

	LangGermanManager(Printable prompt) {
		super(prompt, new GermanConjugationByReversoNet());

		// pre-conjugate encoded irregular verbs (roots)
    	for (String verb : getVerbsOfIrregularConjugations())
   			preconjugate(verb);
	}

	@Override public final LANG getLanguage() { return LANG.de; }

	@Override public Collection<String> getVerbsOfIrregularConjugations() {
		return Konjugierer.defaultKonjugierer.getVerbsOfIrregularConjugations();
	}

	@Override public VerbConjugation conjugate(String verb) {
    	return Konjugierer.conjugate(verb);
    }

    @Override
    public void decline(String params) {
    	final String helpMessage =
    			"The decline command take these values:\n" +
    			"  - a pronoun or article,\n" +
    			"      e.g.  /decl der  \n" +
    			"      e.g.  /decl jeder\n" +
    			"      e.g.  /decl wer  \n" +
    			"      e.g.  /decl ein  \n" +
    			"      e.g.  /decl kein \n" +
    			"      e.g.  /decl mein \n" +
    			"  - a noun,\n" +
    			"      e.g.  /decl R:aumung \n" +
    			"  - an adjective,\n" +
    			"      e.g.  /decl gut \n";

    	if (isEmpty(params)) {
    		promptln(helpMessage);
    		return;
    	}

    	String[] words = params.split(" +");
    	trim(words);
    	enAccent(words);
    	for (String word : reducePersonalPronouns(words)) {
    		List<WORD> tmp = new WordCollection(this).find(word);
    		if (isEmpty(tmp)) {
				if (Character.isUpperCase(word.charAt(0)))
					printNounDeclensions(getPrintable(), null, word);
				else
					printWordDeclensions(getPrintable(), word);
    		} else {
    			for (WORD wd : tmp) {
    				if (wd.isNoun())
    					printNounDeclensions(getPrintable(), wd.getDefaultPOSInfo(), wd.getWord());
    				else if (wd.isVerb())
    					continue;
    				else
    					printWordDeclensions(getPrintable(), wd.getWord());
    			}
    		}
    	}
	}

    @Override
    protected POSInfo parseInflection(String word, POSInfo posInfo, String inflection) {
    	if (isNotEmpty(inflection)) {
	    	if (posInfo.isNoun())
	    		return posInfo.toDeclinable(NomenDeklinationRegel.parse(word, posInfo.getPOS(), inflection));
	    	if (posInfo.isVerb())
	    		return posInfo.toVerb(StarkeVerbKonjugierer.parseVerbInflection(word, inflection));
    	}
   		return super.parseInflection(word, posInfo, inflection);
    }

    private static String[] reducePersonalPronouns(String[] words) {
    	final ArrayList<String> personalPronouns = new ArrayList<String>();
    	if (personalPronouns.size() == 0) {
    		for (String s : sa("ich", "du", "er", "sie", "es", "wir", "ihr"))
    			personalPronouns.add(s);
    	}

    	ArrayList<String> list = new ArrayList<String>();
    	boolean hasPPron = false;
		for (String word : words) {
			if (personalPronouns.contains(word))
   				hasPPron = true;
			else
				list.add(word);
    	}
    	if (hasPPron)
    		list.add("ich");

    	return (list.size() == words.length) ? words : list.toArray(new String[list.size()]);
    }

    public void printNounDeclensions(Printable printable, POSInfo posInfo, String... nouns) {
		MatrixPrinter.Matrix[] matrices = new MatrixPrinter.Matrix[nouns.length];
		int idx = 0;
		for (String n : nouns) {
			DeclensionResult result = null;
			try {
				result = declineNoun(n, posInfo);
			} catch(Exception e) {
				printable.promptln("Unable to decline '" + n + "'.\n");
				return;
			}
			matrices[idx++] = toNounMatrix(result);
		}
	    printMatrix(printable, false, true, matrices, true, false);
    }

    private MatrixPrinter.Matrix toNounMatrix(DeclensionResult result) {
    	String[][] content = result.getResults(getLanguage(), true);
    	int col = -1;
    	switch(result.getGender()) {
    	case m: col = 0; break;
    	case f: col = 1; break;
    	case n: col = 2; break;
    	}
    	int idx = 0;
    	for (DECLENSION decl : DECLENSION.values(getLanguage())) {
    		String[] derDecl = derArticles.get(decl);
    		content[idx][1] = derDecl[col] + " " + content[idx][1];
    		if (content[idx].length > 2)
    			content[idx][2] = derDecl[3]   + " " + content[idx][2];
    		idx++;
    	}
    	return new MatrixPrinter.Matrix(result.getTitles(true), content);
    }

    public static void printAdjAdvComparativeSuperlative(Printable printable, String... words) {
		final String[] titles = { "Adj./Adv.", "Komparativ", "Superlativ I", "Superlativ II" };
		String[][] data = new String[words.length][];
		int i = 0;
		for (String w : words) {
			String[] cs = getComparativeSuperlative(w);
			data[i++] = new String[]{ w, cs[0], cs[1], "am " + cs[1] + 'n' };
		}
		printMatrix(printable, true, titles, data, true, false);
	}

	public void printWordDeclensions(Printable printable, String word) {
		final String[] sampleNounsNom = { "Wein",   "Milch", "Brot",   "Freunde"  };
		final String[] sampleNounsGen = { "Weines", "Milch", "Brotes", "Freunde"  };
		final String[] sampleNounsDat = { "Wein",   "Milch", "Brot",   "Freunden" };
		final String[] sampleNounsAkk = { "Wein",   "Milch", "Brot",   "Freunde"  };

		DeclensionResult[] declResults = declineWord(word);
		
		boolean toHaveNoun = false;
		int artWidth = 3; // "der", ...
		int wordWidth = word.length();
		for (DeclensionResult declResult : declResults) {
			String usage = declResult.getUsage();
			if (usage.equals(ADJ_DER_USAGE))
				toHaveNoun = true;
			if (usage.equals(ADJ_EIN_USAGE)) {
				toHaveNoun = true;
				artWidth = 6; // "keinen"
			}
			wordWidth = declResult.getLongestWordWidth(getLanguage());
		}

		if (toHaveNoun) {
			String fmt = "%" + artWidth + "s %-" + wordWidth + "s [%s]"; // limiter, adjective, sample noun
			DeclensionResult derOrEin = null;
			for (DeclensionResult declResult : declResults) {
				if (declResult.getUsage().equals(ADJ_DER_USAGE))
					derOrEin = derArticles;
				else if (declResult.getUsage().equals(ADJ_EIN_USAGE))
					derOrEin = einLimiters;
				else
					derOrEin = null;
				for (DECLENSION decl : DECLENSION.values(getLanguage())) {
					String[] sa = declResult.get(decl);
					String[] deco = derOrEin == null ? null : derOrEin.get(decl);
					String[] sampleNouns = null;
					switch (decl) {
					case Nominative: sampleNouns = sampleNounsNom; break;
					case Genitive:   sampleNouns = sampleNounsGen; break;
					case Dative:     sampleNouns = sampleNounsDat; break;
					case Accusative: sampleNouns = sampleNounsAkk; break;
					}
					if (sa != null) {
						for (int i=0; i<sa.length; ++i)
							sa[i] = String.format(fmt, at(deco, i, ""), sa[i], sampleNouns[i]);
					}
				}
			}
		}

		MatrixPrinter.Matrix[] matrixSets = new MatrixPrinter.Matrix[declResults.length];
		for (int i=0; i<matrixSets.length; ++i)
			matrixSets[i] = declResults[i].toMatrix(getLanguage());
	    printMatrix(printable, false, true, matrixSets, true, false);
	}

	public static final DeclensionResult derArticles = declineOneSet("d", getDerSuffix(), null, null);
	public static final DeclensionResult einLimiters = declineEinWord("ein", null, null);

    @Override
    public void comparative(String params) {
    	String[] sa = params.split(" +");
    	trim(sa);
    	enAccent(sa);
    	printAdjAdvComparativeSuperlative(getPrintable(), sa);
    }
}
